package com.fingard.dsp.bank.directbank.wzcb01;

import com.fingard.FGBiz;
import com.fingard.constant.Format;
import com.fingard.dsp.bank.directbank.DirectBase;
import com.fingard.net.*;
import com.fingard.text.MD5SaltHelper;
import com.fingard.text.StringHelper;
import com.fingard.util.TransIDHelper;
import com.fingard.xml.XmlTextReader;
import com.fingard.xml.XmlTextWriter;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.io.IOUtils;

import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Date;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 公共部分
 */
public class WZCB01Base extends DirectBase {

    private static TransIDHelper idHelper = new TransIDHelper(1,999);
    private static TransIDHelper idHelper2 = new TransIDHelper(1,99999);
    //文件存放位置
    protected static String localFilePath=StringHelper.trimEndFileSp(FGBiz.dspCenter.dspSet.dataDirPath)
            + File.separator + "WZCB01" + File.separator;

    /**
     * 20位流水号
     * @return
     */
    protected String getQryTransID() {
        String tmpDateNow = Format.DateTime17Format.format(new Date());
        return tmpDateNow +idHelper.getNextIDStr();
    }

    /**
     * 5位流水号
     * @return
     */
    protected String getBatchID() {
        return idHelper2.getNextIDStr();
    }

    /**
     * 组装请求head值
     * @param type 4位 如：E100
     * @param reqType 16位 报文类型代码，如：SQEPay.000.01.01
     * @return
     */
    protected String getHead(String type,String reqType) throws Exception{
        StringBuilder sbd=new StringBuilder();
        sbd.append("LINK");//1-4字段为系统类型，固定填写‘LINK’
        sbd.append("1.00");//5-8字段为系统版本，固定填写‘1.00’
        sbd.append(type);//9-12字段为交易码，例如：‘+E100’
        sbd.append(bankFront.corpNo);//13-20 发送报文的节点ID
        sbd.append("00000000");//21-28 接收报文的节点ID
        sbd.append(Format.DateTime14Format.format(new Date()));//29-42 字段为报文发起方日期和时间 20110910172050
        sbd.append(getQryTransID());//43-62 字段为报文ID，请求报文为MesgID，响应报文为MesgRefID 。同一个交易的请求响应报文ID值相同。
        sbd.append("01");//63-64 两位的转发报文标识：转发报文标识定义同一交易在不同节点中轮转的序号，企业申请报文中为01 ，银行处理后的响应报文为02 。
        sbd.append("00");//65-66 两位的分组报文编号：分组报文编号定义同一业务报文多批发送时的分组编号，对同一笔查询业务进行响应，第一批报文为00，第二批报文为02，依次类推。
        sbd.append(reqType);//67-82 字段为报文类型代码，如：SQEPay.000.01.01。
        String headReq=sbd.toString();
        String macCode= MD5SaltHelper.md5Hex(headReq);
        macCode=macCode.toUpperCase();
        if(macCode.length()>16){
            macCode=macCode.substring(0,16);
        }else {
            throw new Exception("md5加密异常");
        }
        sbd.append(macCode);//83-98 字段为报文头MAC校验码,用MD5进行加密出来后，取加密结果的前16位，要求大写。
        return sbd.toString();
    }

    /**
     * 报文头
     * @param xmlWriter
     * @param p_bankHeader
     */
    protected void writeReqHeader(XmlTextWriter xmlWriter, WZCB01Header p_bankHeader){
        xmlWriter.writeStartElement("Request");//请求报文头
        xmlWriter.writeStartElement("Head");//报文头节点
        xmlWriter.writeElementString("TxCode",p_bankHeader.txCode);//对应交易码
        xmlWriter.writeElementString("MerchantNo",bankFront.corpNo);//填写商户代码
        xmlWriter.writeElementString("OperNo",bankFront.operator);//填写商户交易操作员
        Date date=new Date();
        xmlWriter.writeElementString("TransDate",Format.Date10Format.format(date));//交易日期　
        xmlWriter.writeElementString("TransTime",Format.Time6Format.format(date));//交易时间
        xmlWriter.writeElementString("OrderNo",p_bankHeader.orderNo);//发起方提供的唯一业务标识
        xmlWriter.writeEndElement();
    }

    /**
     * 报文编码
     * @return
     */
    protected String getCharset(){
        return getBankConfig().getCharset("GBK");
    }

    /**
     * 请求银行
     * @param p_reqBody
     * @return
     * @throws Exception
     */
    protected String[] sendToBank(String p_reqBody,String head) throws Exception {
        String[] retStr = new String[]{"", ""};
        //1.获取message签名
        String tmpAddrSign = "http://" + bankFront.serverIP + ":" + bankFront.signPort;
        WriteBankLogLn("提交签名的地址：" + tmpAddrSign);
        WriteBankLogLn("提交签名的报文：" + p_reqBody);
        WebRequest tmpWebRequestSign = new WebRequest(tmpAddrSign);
        tmpWebRequestSign.setConnectTimeout(60000);
        tmpWebRequestSign.setRequestProperty("Content-Type", "INFOSEC_SIGN/1.0");
        tmpWebRequestSign.setRequestProperty("connection", "Close");
        byte[] tmpToSignBytes = p_reqBody.getBytes(getCharset());
        String[] tmpSignStr = tmpWebRequestSign.upload(tmpToSignBytes, getCharset());
        WriteBankLogLn("签名返回的结果："+tmpSignStr[1].replaceAll("\r\n",""));
        if(tmpSignStr[0].length() == 0){
            StringBuilder tmpSb2 = new StringBuilder();
            XmlTextReader tmpBkReader2 = loadBankRespXmlReader(tmpSignStr[1], tmpSb2);
            if (tmpBkReader2 != null) {
                String result = tmpBkReader2.getFirstText("/html/head/result");
                String sign=tmpBkReader2.getFirstText("/html/body/sign");
                if("0".equals(result) && StringHelper.hasAnyChar(sign)){
                    //2.发送报文至银行
                    String reqBankUrl = bankFront.serverURL;
                    WriteBankLogLn("请求银行的地址：" + reqBankUrl);
                    sign=Base64.encodeBase64String(sign.getBytes(getCharset()));
                    String reqStr="channel=yqzl&head="+head+"&message="+sign;
                    WriteBankLogLn("请求银行的报文：" + reqStr);
                    WebRequest tmpWebRequest = new WebRequest(reqBankUrl);
                    tmpWebRequest.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                    tmpWebRequest.setRequestProperty("connection", "Close");
                    tmpWebRequest.setConnectTimeout(60000);
                    byte[] tmpToBytes = reqStr.getBytes(getCharset());
                    String[] tmpStr = tmpWebRequest.upload(tmpToBytes, getCharset());
                    WriteBankLogLn("银行返回的结果："+tmpStr[1].replaceAll("\r\n",""));
                    //3.接收银行响应信息并验签
                    if(tmpStr[0].length() == 0){
                        StringBuilder tmpSb = new StringBuilder();
                        XmlTextReader tmpBkReader = loadBankRespXmlReader(tmpStr[1], tmpSb);
                        if (tmpBkReader != null) {
                            String rspMessage = tmpBkReader.getFirstText("/response/message");
                            if(StringHelper.hasAnyChar(rspMessage)){
                                WriteBankLogLn("请求银行验签数据："+rspMessage.replaceAll("\r\n",""));
                                WebRequest tmpWebCheckSign = new WebRequest(tmpAddrSign);
                                tmpWebCheckSign.setConnectTimeout(60000);
                                tmpWebCheckSign.setRequestProperty("Content-Type", "INFOSEC_VERIFY_SIGN/1.0");
                                tmpWebCheckSign.setRequestProperty("connection", "Close");
                                byte[] tmpToCheckBytes = Base64.decodeBase64(rspMessage);
                                String[] rspStr = tmpWebCheckSign.upload(tmpToCheckBytes, getCharset());
                                WriteBankLogLn("验签返回的结果："+rspStr[1].replaceAll("\r\n",""));
                                if(rspStr[0].length()==0){
                                    StringBuilder tmpSb3 = new StringBuilder();
                                    XmlTextReader tmpBkReader3 = loadBankRespXmlReader(rspStr[1], tmpSb3);
                                    if (tmpBkReader3 != null) {
                                        String result2 = tmpBkReader3.getFirstTextNotNull("/html/head/result");
                                        String sic=rspStr[1].substring(rspStr[1].indexOf("<sic>")+"<sic>".length(),rspStr[1].indexOf("</sic>"));
                                        if("0".equals(result2) && StringHelper.hasAnyChar(sic)){
                                            retStr[1]=sic;
                                        }else {
                                            retStr[0] = ConnState.FAIL;
                                            retStr[1] = "验签失败";
                                            return retStr;
                                        }
                                    }else {
                                        retStr[0] = ConnState.FAIL;
                                        retStr[1] = "验签失败";
                                        return retStr;
                                    }
                                }else {
                                    retStr[0] = ConnState.FAIL;
                                    retStr[1] = "验签失败";
                                    return retStr;
                                }
                            }else {
                                retStr[0] = ConnState.FAIL;
                                retStr[1] = "验签失败";
                                return retStr;
                            }
                        }else {
                            retStr[0] = ConnState.FAIL;
                            retStr[1] = "通讯异常:"+tmpStr[1];
                            return retStr;
                        }
                    }else {
                        retStr[0] = ConnState.FAIL;
                        retStr[1] = "通讯异常:"+tmpStr[1];
                        return retStr;
                    }
                }else {
                    retStr[0] = ConnState.FAIL;
                    retStr[1] = "签名失败";
                    return retStr;
                }
            }else {
                retStr[0] = ConnState.FAIL;
                retStr[1] = "签名失败";
                return retStr;
            }
        }else {
            retStr[0] = ConnState.FAIL;
            retStr[1] = "签名失败";
            return retStr;
        }
        return retStr;
    }

    /**
     * 文件上传
     */
    protected boolean FileUpload(String fileTxtPath,String fileZipPath,String mer_id) {
        boolean flag=false;//文件摘要
        String threeDesKey=getBankConfig().getValueAsString("threeDesKey");
        HttpClient client = null;
        PostMethod method = null;
        try {
            //压缩文件
            File encryptFile = new File(fileZipPath);
            OutputStream outputStream = new FileOutputStream(encryptFile);
            CipherOutputStream cos = encryptMode(threeDesKey.getBytes(), outputStream);
            ZipOutputStream zos = new ZipOutputStream(cos);
            zos.putNextEntry(new ZipEntry(new File(fileTxtPath).getName()));
            IOUtils.copy(new FileInputStream(new File(fileTxtPath)), zos);
            zos.close();
            String uploadUrl = "/upload/" + encryptFile.getName() + "?mer_id="+mer_id;
            //获取sign
            String signValue = getSign("/wzelink"+uploadUrl);
            //上传文件地址
            String postUrl = bankFront.serverUpload + uploadUrl + "&sign=" + signValue.toUpperCase();
            WriteBankUrlLogLn(postUrl);
            //文件上传请求
            client = new HttpClient();
            method = new PostMethod(postUrl);
            Part[] parts = {new FilePart(encryptFile.getName(), encryptFile, "application/zip", "GBK")};
            method.setRequestEntity(new MultipartRequestEntity(parts, method.getParams()));
            method.setRequestHeader("cache-control", "no-cache");
            client.getHttpConnectionManager().getParams().setConnectionTimeout(6000);
            int status = client.executeMethod(method);
            if (status == HttpStatus.SC_OK) {
                String response = method.getResponseBodyAsString();
                if(response.contains("<message>")){
                    String message=response.substring(response.indexOf("<message>")+"<message>".length(),response.indexOf("</message>"));
                    String rspContext=new String(Base64.decodeBase64(message),getCharset());
                    WriteBankLogStep3(rspContext);
                    StringBuilder tmpSb = new StringBuilder();
                    XmlTextReader tmpBkReader = loadBankRespXmlReader(rspContext, tmpSb);
                    if (tmpBkReader != null) {
                        String resultCode=tmpBkReader.getFirstTextNotNull("/Response/Head/RefuseCode");
                        //String resultMesg=tmpBkReader.getFirstTextNotNull("/Response/Head/RefuseReason");
                        if("0000".equals(resultCode)){
                            flag=true;
                        }
                    }
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (HttpException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放连接
            if (method != null) {
                method.releaseConnection();
            }
        }
        return flag;
    }

    /**
     * 获取文件摘要
     * @param filePath
     * @return
     */
    protected String getSignByFilePath(String filePath){
        String sign;
        try {
            FileInputStream fins=new FileInputStream(filePath);
            ByteArrayOutputStream baos=new ByteArrayOutputStream();
            IOUtils.copy(fins,baos);
            MessageDigest messageDigest=MessageDigest.getInstance("SHA-1");
            messageDigest.update(baos.toByteArray());
            byte[] digest=messageDigest.digest();
            sign=new String(Hex.encodeHex(digest)).toUpperCase();
        }catch (Exception e){
            return "";
        }
        return sign;
    }

    /**
     * 文件下载
     */
    protected File FileDownload(String rspFileName) {
        String fileDownloadUrl= bankFront.serverUpload+"/download/"+rspFileName;
        WebRequest tmpWebCheckSign = new WebRequest(fileDownloadUrl);
        tmpWebCheckSign.setConnectTimeout(60000);
        tmpWebCheckSign.setRequestProperty("connection", "Close");
        tmpWebCheckSign.download(localFilePath+rspFileName);
        File file=new File(localFilePath+rspFileName);
        if(file.exists()){
            return file;
        }
        return null;
    }

    /**
     *	encrypt To File
     *	@param 	keybyte
     *	@return
     */
    public static CipherOutputStream encryptMode(byte[] keybyte,OutputStream oustr) {
        String Algorithm = "DESede";
        try {
            SecretKey deskey = new SecretKeySpec(keybyte, Algorithm);
            Cipher c1 = Cipher.getInstance(Algorithm);
            c1.init(Cipher.ENCRYPT_MODE, deskey);
            CipherOutputStream cos = new CipherOutputStream(oustr, c1);
            return cos;
        } catch (java.security.NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        } catch (javax.crypto.NoSuchPaddingException e2) {
            e2.printStackTrace();
        } catch (Exception e3) {
            e3.printStackTrace();
        }
        return null;
    }

    /**
     * 获取sign
     * @param msg
     * @return
     * @throws Exception
     */
    public String getSign(String msg) throws Exception{
        String pfxFilePath=getBankConfig().getValueAsString("pfxFilePath");
        String pfxPass=getBankConfig().getValueAsString("pfxPass");
        PrivateKey privateKey=getPvkformPfx(pfxFilePath,pfxPass);
        try {
            if(privateKey==null){
                return null;
            }else {
                Signature signer=Signature.getInstance("SHA1withRSA");
                signer.initSign(privateKey);
                signer.update(msg.getBytes());
                byte[] signdata=signer.sign();
                return bytesToHexString(signdata);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取私钥
     * @param pfxFilePath
     * @param pfxPass
     * @return
     * @throws Exception
     */
    public static PrivateKey getPvkformPfx(String pfxFilePath,String pfxPass) throws Exception{
        PrivateKey privateKey=null;
        try {
            KeyStore ks=KeyStore.getInstance("PKCS12");
            FileInputStream fis=new FileInputStream(pfxFilePath);
            char[] nPassword=null;
            if(pfxPass==null || pfxPass.trim().equals("")){
                nPassword=null;
            }else {
                nPassword=pfxPass.toCharArray();
            }
            ks.load(fis,nPassword);
            fis.close();
            String keyAlias=ks.aliases().nextElement().toString();
            privateKey=(PrivateKey) ks.getKey(keyAlias,nPassword);
            return privateKey;
        }catch (Exception e){
            e.printStackTrace();
            throw new Exception("Get private key from the PFX file ["+pfxFilePath+"] error! Exception:"+e.toString());
        }
    }

    private static  String bytesToHexString(byte[] src){
        StringBuilder stringBuilder=new StringBuilder();
        if(src==null || src.length<=0){
            return null;
        }
        for (int i=0;i<src.length;i++){
            int v=src[i] & 0xFF;
            String hv=Integer.toHexString(v);
            if(hv.length()<2){
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }
}
